﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Awesomium.Windows.Forms;
using Awesomium.Core;
using ServiceStack.Text;
using fastJSON;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using System.Diagnostics;

namespace RSuite
{
    public class AwesomiumForm : WebControl
    {
        private string url;

        public AwesomiumForm(string url)
            : base()
        {
            this.url = url;
        }

        public Form CreateForm()
        {
            var form = new Form();
            form.Controls.Add(this);
            form.Load += new EventHandler(delegate(object sender, EventArgs e)
            {
                this.NavigateLocal(this.url);
            });

            this.InitForm(form);

            base.Dock = DockStyle.Fill;

            return form;
        }

        protected virtual void InitForm(Form form) { }

        protected override void OnLoadCompleted(object sender, EventArgs e)
        {
            base.OnLoadCompleted(sender, e);

            base.CreateObject("Awesomium");
            base.SetObjectCallback("Awesomium", "InvokeMethod", this.InvokeMethod);
        }

        private void InvokeMethod(object sender, JSCallbackEventArgs e)
        {
            var method = e.Arguments[0].ToString();
            var parameters = e.Arguments[1].ToString();
            var id = e.Arguments[2].ToString();
            var useThread = e.Arguments[3].ToBoolean();

            var data = string.IsNullOrEmpty(parameters) ? string.Empty : this.FromJSON(parameters);
            if (!useThread)
            {
                var result = this.InvokeLocalMethod(method, data, id);
                if (result == null)
                {
                    result = this.InvokeCommonMethod(method, data);
                }
                this.JobDone(id, result);
            }
            else
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object d)
                {
                    var result = this.InvokeLocalMethod(method, data, id);
                    this.JobDone(id, result);
                }));
            }
        }

        protected virtual object InvokeLocalMethod(string method, dynamic data, string id)
        {
            return null;
        }

        protected virtual object InvokeCommonMethod(string method, dynamic data)
        {
            switch (method)
            {
                case "alert":
                    return this.Alert(data);
                case "browse-dialog":
                    return this.BrowseDialog(data);
                case "open-folder":
                    Process.Start(data);
                    return string.Empty;
                case "open-file":
                    Process.Start("explorer.exe", string.Format("/select,\"{0}\"", data));
                    return string.Empty;
                default:
                    return string.Empty;
            }
        }

        public string Alert(string message)
        {
            MessageBox.Show(message);
            return string.Empty;
        }

        private object BrowseDialog(string path)
        {
            var dialog = new FolderBrowserDialog();
            dialog.SelectedPath = path;
            if (dialog.ShowDialog() != DialogResult.OK)
            {
                return string.Empty;
            }

            return new { selectedPath = dialog.SelectedPath };
        }

        protected DialogResult ShowDialog(CommonDialog dialog)
        {
            return dialog.ShowDialog();
        }

        public string ToJSON(object obj)
        {
            return JsonSerializer.SerializeToString(obj);
        }

        public dynamic FromJSON(string json)
        {
            return JSON.Instance.Parse(json);
        }

        public void NavigateLocal(string relative)
        {
            var file = new FileInfo(relative);
            base.LoadURL(new Uri(file.FullName).AbsoluteUri);
        }

        private delegate void JobDoneDelegate(string id, object data);
        protected void JobDone(string id, object data)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new JobDoneDelegate(this.JobDone), id, data);
                return;
            }

            base.CallJavascriptFunction("Async", "finishTask", new JSValue(id), new JSValue(this.ToJSON(data)));
        }

        private delegate void JobUpdateDelegate(string id, object data);
        protected void JobUpdate(string id, object data)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new JobUpdateDelegate(this.JobUpdate), id, data);
                return;
            }

            base.CallJavascriptFunction("Async", "updateTask", new JSValue(id), new JSValue(this.ToJSON(data)));
        }
    }
}
